{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# FFI 错误处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pytest\n",
    "import platform\n",
    "from tvm import ffi as tvm_ffi\n",
    "\n",
    "\n",
    "def test_parse_traceback():\n",
    "    traceback = \"\"\"\n",
    "    File \"test.py\", line 1, in <module>\n",
    "    File \"test.py\", line 3, in run_test\n",
    "    \"\"\"\n",
    "    parsed = tvm_ffi.error._parse_traceback(traceback)\n",
    "    assert len(parsed) == 2\n",
    "    assert parsed[0] == (\"test.py\", 1, \"<module>\")\n",
    "    assert parsed[1] == (\"test.py\", 3, \"run_test\")\n",
    "\n",
    "\n",
    "def test_error_from_cxx():\n",
    "    test_raise_error = tvm_ffi.get_global_func(\"testing.test_raise_error\")\n",
    "\n",
    "    try:\n",
    "        test_raise_error(\"ValueError\", \"error XYZ\")\n",
    "    except ValueError as e:\n",
    "        assert e.__tvm_ffi_error__.kind == \"ValueError\"\n",
    "        assert e.__tvm_ffi_error__.message == \"error XYZ\"\n",
    "        assert e.__tvm_ffi_error__.traceback.find(\"TestRaiseError\") != -1\n",
    "\n",
    "    fapply = tvm_ffi.convert(lambda f, *args: f(*args))\n",
    "\n",
    "    with pytest.raises(TypeError):\n",
    "        fapply(test_raise_error, \"TypeError\", \"error XYZ\")\n",
    "\n",
    "    # wrong number of arguments\n",
    "    with pytest.raises(TypeError):\n",
    "        tvm_ffi.convert(lambda x: x)()\n",
    "\n",
    "\n",
    "@pytest.mark.skipif(\n",
    "    \"32bit\" in platform.architecture(),\n",
    "    reason=\"libbacktrace file name support is not available in i386 yet\",\n",
    ")\n",
    "def test_error_from_nested_pyfunc():\n",
    "    fapply = tvm_ffi.convert(lambda f, *args: f(*args))\n",
    "    cxx_test_raise_error = tvm_ffi.get_global_func(\"testing.test_raise_error\")\n",
    "    cxx_test_apply = tvm_ffi.get_global_func(\"testing.apply\")\n",
    "\n",
    "    record_object = []\n",
    "\n",
    "    def raise_error():\n",
    "        try:\n",
    "            fapply(cxx_test_raise_error, \"ValueError\", \"error XYZ\")\n",
    "        except ValueError as e:\n",
    "            assert e.__tvm_ffi_error__.kind == \"ValueError\"\n",
    "            assert e.__tvm_ffi_error__.message == \"error XYZ\"\n",
    "            assert e.__tvm_ffi_error__.traceback.find(\"TestRaiseError\") != -1\n",
    "            record_object.append(e.__tvm_ffi_error__)\n",
    "            raise e\n",
    "\n",
    "    try:\n",
    "        cxx_test_apply(raise_error)\n",
    "    except ValueError as e:\n",
    "        traceback = e.__tvm_ffi_error__.traceback\n",
    "        assert e.__tvm_ffi_error__.same_as(record_object[0])\n",
    "        assert traceback.count(\"TestRaiseError\") == 1\n",
    "        assert traceback.count(\"TestApply\") == 1\n",
    "        assert traceback.count(\"<lambda>\") == 1\n",
    "        pos_cxx_raise = traceback.find(\"TestRaiseError\")\n",
    "        pos_cxx_apply = traceback.find(\"TestApply\")\n",
    "        pos_lambda = traceback.find(\"<lambda>\")\n",
    "        assert pos_cxx_raise > pos_lambda\n",
    "        assert pos_lambda > pos_cxx_apply\n",
    "\n",
    "\n",
    "def test_error_traceback_update():\n",
    "    fecho = tvm_ffi.get_global_func(\"testing.echo\")\n",
    "\n",
    "    def raise_error():\n",
    "        raise ValueError(\"error XYZ\")\n",
    "\n",
    "    try:\n",
    "        raise_error()\n",
    "    except ValueError as e:\n",
    "        ffi_error = tvm_ffi.convert(e)\n",
    "        assert ffi_error.traceback.find(\"raise_error\") != -1\n",
    "\n",
    "    def raise_cxx_error():\n",
    "        cxx_test_raise_error = tvm_ffi.get_global_func(\"testing.test_raise_error\")\n",
    "        cxx_test_raise_error(\"ValueError\", \"error XYZ\")\n",
    "\n",
    "    try:\n",
    "        raise_cxx_error()\n",
    "    except ValueError as e:\n",
    "        assert e.__tvm_ffi_error__.traceback.find(\"raise_cxx_error\") == -1\n",
    "        ffi_error1 = tvm_ffi.convert(e)\n",
    "        ffi_error2 = fecho(e)\n",
    "        assert ffi_error1.traceback.find(\"raise_cxx_error\") != -1\n",
    "        assert ffi_error2.traceback.find(\"raise_cxx_error\") != -1\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "ai",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.12.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
